#include "./../../../include/online/Reactor/ThreadPool.h"
#include "./../../../include/online/CacheSystem/TimerManager.h"
#include "./../../../include/online/Reactor/Thread.h"
#include <iostream>

extern __thread const char *name;

using namespace std;

ThreadPool::ThreadPool(size_t threadNum, size_t quesize)
    : _threadNum(threadNum), _quesize(quesize), _taskque(_quesize), _isExit(false)
{
    _threads.reserve(_threadNum);
}

ThreadPool::~ThreadPool()
{
}

// 线程池启动函数
// 线程启动就是创建线程，并且将其放在容器中进行存储，同时执行doTask函数（工作线程的入口函数）
void ThreadPool::start()
{
    // 需要创建出线程
    // 线程创建出来之后需要执行线程处理函数
    for (size_t idx = 0; idx < _threadNum; ++idx)
    {
        unique_ptr<Thread> up(new Thread(std::bind(&ThreadPool::doTask, this), std::to_string(idx)));
        _threads.push_back(std::move(up));
    }
    unique_ptr<Thread> up(new Thread(std::bind(&TimerManager::start, TimerManager::getTimerManager()), std::to_string(0)));
    _threads.push_back(std::move(up));
    for (auto &th : _threads)
    {
        th->start();
    }
}

// 线程池关闭函数
void ThreadPool::stop()
{
    // 需要保证任务队列中的所有任务执行完毕之后，主线程才能继续向下执行
    // 否则就卡在此处
    while (!_taskque.empty())
    {
        std::this_thread::sleep_for(chrono::milliseconds(1));
    }

    // 线程池要退出来了，将标志设置为true
    // 然后回收所有工作线程
    _isExit = true;

    // 此时需要唤醒所有的睡眠线程
    // 解决方法二：
    // 往任务队列中加入空任务，多少个子线程（工作线程）就加入多少个空任务
    // 用空任务进行唤醒所有睡眠的线程
    for (size_t i = 0; i != _threadNum; ++i)
    {
        _taskque.push(nullptr);
    }

    for (auto &th : _threads)
    {
        th->stop(); // 回收子线程
    }
}

void ThreadPool::addTask(Task &&task)
{
    if (task)
    {
        _taskque.push(std::move(task));
    }
}

// 获取到任务队列中的任务
Task ThreadPool::getTask()
{
    return _taskque.pop();
}

void ThreadPool::doTask()
{
    /*
    子线程在getTask获取完任务之后，那么任务队列就会为空，那么
    子线程（工作线程）会继续向下执行，去处理任务，也就是调用process函数，
    而主线程在执行stop函数的时候，发现任务队列为空，那么主线程会继续向下执行；
    如果出现主线程向下执行的速度比较快，也就是子线程（工作线程）执行process函数
    的速度比较慢，那么主线程很快将标志位_isExit设置为true，那么当子线程
    执行完处理任务函数之后就不会再进入到while循环中，那么主线程
    就可以将子线程（工作线程）回收，那么整个程序就可以退出来。

    如果主线程继续向下执行的速度比较慢，也就是在将_isExit设置为true之前，
    即子线程执行的速度比较快，有可能出现子线程在执行完process之后，
    又进入到while循环中，那么此时主线再将_isExit标志位设置为true，
    就晚了，此时子线程会继续执行getTask进行获取任务，而任务队列是空的，
    所以此时子线程会处于睡眠状态，主线程无法回收子线程。此时有两种解决方式：
    方式一：设置为友元类，唤醒所有在对应条件变量睡眠的线程
    方式二：写一个函数，唤醒所有在对应条件变量睡眠的线程
    方式三：用空任务进行唤醒，多少个工作线程就加入多少个空任务
    */
    while (!_isExit)
    {
        // 先拿到任务
        /*
        getTask函数获取的任务就是通过addTask传递进来的，
        而addTask传递进来的就是通过bind绑定的process函数，
        所以此处获取的并且执行的任务就是process
        */
        Task task = this->getTask();
        // 处理任务
        if (task)
        {
            task();
        }
        // else
        // {
        //     cout << "Task nullptr" << endl;
        // }
    }
}